<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <title>One To One - Shared Primary Key</title>
</head>
<body>
  <h3>One To One - Shared Primary Key Association</h3>
  A one-to-one association can be mapped using shared primary key. For example, mapping a user
  with his profile. Many web applications have such a requirement, where User entity maintains security
  related information such as invalid password attempts, security questions etc. and Profile entity maintains data
  such as emails and contact numbers for the user. Since a user will ever have a single profile, it's a one to
  one association and a good candidate for shared primary key association due to closely tied nature of entities
  involved.
  <p>In our example code, a Profile object maintains reference to User, making it a unidirectional association . The User
  entity mapping is the usual stuff without any surprises. The Profile entity has an Id attribute and one thing to note
  about it is that it's not auto-generated. It can be deduced by the lack of <em>@GeneratedValue</em> annotation on it.
  It also maintains a user reference as shown below -
  <pre class="brush: java">
@MapsId 
@OneToOne 
public User getUser() {
  return user;
}    
  </pre>
  The annotations worth noting here are - <em>@OneToOne</em> and <em>@MapsId</em>.
  </p>
  <ul>
    <li><em>@OneToOne</em></li>
    This annotation defines the relationship between User and Profile entities as One to One.
    <li><em>@MapsId</em></li>
    This annotation specifies that identifier for this entity i.e Profile is also a foreign key to User entity table
    and it's value should be sourced from the other entity in the relationship i.e User.
    Therefore, as mentioned earlier, <em>@GeneratedValue</em> annotation is absent on Id attribute of Profile
    entity since it would be copied from User entity. If column name for Id attribute is something other than the name
    of the Id attribute itself, it can be customized using <em>@JoinTable</em> annotation. For example, if Id attribute
    of User entity is mapped to <q>USER_ID</q> column, the corresponding mapping in Profile entity will be as follows -
  <pre class="brush: java">
@MapsId 
@OneToOne
@JoinColumn(name = "USER_ID")
public User getUser() {
  return user;
}    
  </pre>
  </ul>
  As the User entity doesn't maintain reference to Profile, how can it get the one associated with it?
  It can simply make a JPA QL query like one shown below to find the Profile by id using it's own identifier value
  since the primary key is shared between these two entities -
  <pre class="brush: java">
@NamedQuery(name = "getProfileForUser", 
	query = "SELECT p FROM OtoSharedPkProfile p WHERE p.id = :userId")    
  </pre>
  <h4>Making the association bi-directional</h4>
  Instead of using a JPA QL query to fetch profile for a user, we can make the relationship bi-directional. It can
  be done by keeping reference to Profile in User as shown below -
  <pre class="brush: java">
@OneToOne(mappedBy = "user")
public Profile getProfile() {
  return profile;
} 
  </pre>
  The <em>mappedBy</em> attribute of the <em>@OnetoOne</em> annotation tells that this is the inverse side of the
  relationship and the owner of this association is user attribute of Profile entity.
  <p></p>
  <h3>Mapping without an explicit Id attribute in one of the entities</h3>
  An explicit Id attribute can easily be omitted from one of the entities participating in such a relationship,
  since it can be easily fetched using the identifier of the other entity. For example,
  we can remove Id attribute from Profile entity in our example as shown below -
  <pre class="brush: java">
@Entity(name = "OtoSharedPkNoIdProfile") 
@Table(name = "oto_sharedpk_noid_profile")
public class Profile implements Serializable {

	private String email;
	private User user;
	
	public Profile() {}
	
	public Profile(String email, User user) {
		this.email = email;
		this.user = user;
	}
	
	@Column(nullable = false, length = 30)
	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	@Id 
	@OneToOne
	public User getUser() {
		return user;
	}

	public void setUser(User user) {
		this.user = user;
	}	
}    
  </pre>
  
  Such Profile can then be fetched using following JPA QL query -
  <pre class="brush: java">
@NamedQuery(name = "getProfileByUserId", 
		query = "SELECT p FROM OtoSharedPkNoIdProfile p WHERE p.user.id = :userId")    
  </pre>
  Full example code for this scenario can be found in the accompanying examples project.
</body>
</html>
